package com.wanghua.mall.secKill.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.wanghua.common.dto.mq.SecKillOrderDto;
import com.wanghua.common.utils.R;
import com.wanghua.common.vo.UserResponseVo;
import com.wanghua.mall.secKill.Dto.SkuRedisDto;
import com.wanghua.mall.secKill.constant.RedisConstant;
import com.wanghua.mall.secKill.interceptor.SecKillInterceptor;
import com.wanghua.mall.secKill.openfeign.CouponClient;
import com.wanghua.mall.secKill.openfeign.ProductClient;
import com.wanghua.mall.secKill.service.SecKillService;
import com.wanghua.mall.secKill.vo.SeckillSessionVo;
import com.wanghua.mall.secKill.vo.SkuInfoVo;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author wanghua
 * @create 2021-09-11 15:53
 */
@Service
public class SecKillServiceImpl implements SecKillService {

    @Resource
    public CouponClient couponClient;
    @Resource
    private ProductClient productClient;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    public StringRedisTemplate stringRedisTemplate;
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 上架最近三天需要三天秒杀的商品
     */
    @Override
    public void uploadSecKillSkuLatestThreeDay() {
        //1、扫描最近三天的商品需要参加秒杀的活动
        R r = couponClient.getLatestThreeDaySession();
        if (r.getCode() == 0) {
            List<SeckillSessionVo> sessionVos = (List<SeckillSessionVo>) r.get("data");
            //redis缓存活动信息
            //redis缓存活动商品信息
        }
    }

    /**
     * 获取当前正在秒杀的商品
     *
     * @return
     */
    @Override
    public List<SkuRedisDto> getCurrentSecKillSkus() {
        //1.确定当前时间属于哪个秒杀场次
        long time = new Date().getTime();
        Set<String> keys = stringRedisTemplate.keys(RedisConstant.SESSION_CACHE_PREFIX + "*");
        for (String key : keys) {
            String[] strings = key.replaceAll(RedisConstant.SESSION_CACHE_PREFIX, "").split("_");
            long startTime = Long.parseLong(strings[0]);
            long endTime = Long.parseLong(strings[1]);
            if (time >= startTime && time <= endTime) {
                List<String> range = stringRedisTemplate.opsForList().range(key, -100, 100);
                BoundHashOperations<String, String, String> operations = stringRedisTemplate
                        .boundHashOps(RedisConstant.SECKILL_CACHE_PREFIX);
                List<String> list = operations.multiGet(range);
                if (list != null && list.size() > 0) {
                    return list.stream().map((item) -> JSON.parseObject(item, SkuRedisDto.class)).collect(Collectors.toList());
                }
                break;
            }
        }
        return null;
    }

    /**
     * 获得秒杀的商品详细信息
     *
     * @param skuId
     * @return
     */
    @Override
    public SkuRedisDto getSecKillSkuInfo(Long skuId) {
        //1.找到所有参与秒杀的商品key
        BoundHashOperations<String, String, String> operations = stringRedisTemplate.boundHashOps(RedisConstant.SECKILL_CACHE_PREFIX);
        Set<String> keys = operations.keys();
        String regex = "\\d_" + skuId;
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                if (Pattern.matches(regex, key)) {
                    String s = operations.get(key);
                    SkuRedisDto skuRedisDto = JSON.parseObject(s, SkuRedisDto.class);
                    long time = new Date().getTime();
                    if (time >= skuRedisDto.getStartTime() && time <= skuRedisDto.getEndTime()) {
                    } else {
                        skuRedisDto.setRandomCode(null);
                    }
                    return skuRedisDto;
                }
            }
        }
        return null;
    }

    /**
     * 秒杀商品
     *
     * @param killId
     * @param key
     * @param num
     * @return orderNo订单号
     */
    @Override
    public String secKill(String killId, String key, Integer num) {
        UserResponseVo userResponseVo = SecKillInterceptor.threadLocal.get();

        BoundHashOperations<String, String, String> operations = stringRedisTemplate.boundHashOps(RedisConstant.SECKILL_CACHE_PREFIX);
        String json = operations.get(killId);
        if (StringUtils.hasLength(json)) {
            SkuRedisDto skuRedisDto = JSON.parseObject(json, SkuRedisDto.class);
            //1.校验合法性
            //1.1校验时间
            long time = new Date().getTime();
            if (time >= skuRedisDto.getStartTime() && time <= skuRedisDto.getEndTime()) {
                //1.2校验随机码和商品id
                if (skuRedisDto.getRandomCode().equals(key) && (skuRedisDto.getPromotionSessionId() + "_" + skuRedisDto.getSkuId()).equals(killId)) {
                    //1.3验证购物数量
                    if (skuRedisDto.getSeckillLimit().intValue() >= num) {
                        //1.4验证这个人是否买过了
                        String redisKey = userResponseVo.getId() + "_" +
                                skuRedisDto.getPromotionSessionId() + "_" + skuRedisDto.getSkuId();
                        Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(),
                                skuRedisDto.getEndTime() - time, TimeUnit.MILLISECONDS);
                        if (absent) { //占位成功
                            RSemaphore semaphore = redissonClient.getSemaphore(RedisConstant.SKU_STOCK_SEMAPHORE + key);
                            try {
                                semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
                                //秒杀成功，快速下单，发送MQ消息
                                String orderNo = IdWorker.getTimeId();
                                SecKillOrderDto secKillOrderDto = new SecKillOrderDto();
                                secKillOrderDto.setOrderNo(orderNo);
                                secKillOrderDto.setUserId(userResponseVo.getId());
                                secKillOrderDto.setNum(num);
                                secKillOrderDto.setSeckillPrice(skuRedisDto.getSeckillPrice());
                                secKillOrderDto.setPromotionSessionId(skuRedisDto.getPromotionSessionId());
                                secKillOrderDto.setSkuId(skuRedisDto.getSkuId());
                                rabbitTemplate.convertAndSend("order.event.exchange", "order.secKill.order", secKillOrderDto);
                                return orderNo;
                            } catch (InterruptedException e) {
                                return null;
                            }

                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * redis缓存活动信息
     *
     * @param sessionVos
     */
    private void saveSessionVo(List<SeckillSessionVo> sessionVos) {
        sessionVos.forEach(sessionVo -> {
            long startTime = sessionVo.getStartTime().getTime();
            long endTime = sessionVo.getEndTime().getTime();
            String key = RedisConstant.SESSION_CACHE_PREFIX + startTime + "_" + endTime;
            if (stringRedisTemplate.hasKey(key)) {
                return;
            }
            List<String> skuIdList = sessionVo.getRelationSkuList().stream()
                    .map((relationSku) -> relationSku.getPromotionSessionId() + "_" + relationSku.getSkuId().toString())
                    .collect(Collectors.toList());
            stringRedisTemplate.opsForList().leftPushAll(key, skuIdList);
        });

    }

    /**
     * redis缓存活动商品信息
     *
     * @param sessionVos
     */
    private void saveSessionSku(List<SeckillSessionVo> sessionVos) {
        //准备hash操作，绑定hash
        BoundHashOperations<String, String, String> operations = stringRedisTemplate
                .boundHashOps(RedisConstant.SECKILL_CACHE_PREFIX);
        sessionVos.forEach(sessionVo -> {
            sessionVo.getRelationSkuList().forEach((relationSku) -> {
                if (!operations.hasKey(relationSku.getSkuId().toString())) {
                    SkuRedisDto skuRedisDto = new SkuRedisDto();
                    BeanUtils.copyProperties(relationSku, skuRedisDto);
                    SkuInfoVo skuInfoVo = JSON.parseObject(productClient
                            .getSkuInfo(relationSku.getSkuId()), SkuInfoVo.class);
                    skuRedisDto.setSkuInfoVo(skuInfoVo);
                    skuRedisDto.setStartTime(sessionVo.getStartTime().getTime());
                    skuRedisDto.setEndTime(sessionVo.getEndTime().getTime());
                    //随机码
                    String randomCode = UUID.randomUUID().toString().replaceAll("-", "");
                    skuRedisDto.setRandomCode(randomCode);
                    //存储
                    operations.put(relationSku.getPromotionSessionId() + "_" + relationSku.getSkuId().toString(),
                            JSON.toJSONString(skuRedisDto));
                    //
                    //商品数量作为信号量->限流
                    RSemaphore semaphore = redissonClient.getSemaphore(RedisConstant.SKU_STOCK_SEMAPHORE + randomCode);
                    semaphore.trySetPermits(relationSku.getSeckillCount().intValue());
                }
            });
        });

    }
}
